from django.test import TestCase

# Create your tests here.
from django.test import TestCase, tag
from rest_framework.test import APITestCase,APITransactionTestCase
# Create your tests here.
from .models import *
from .serializers import *
from accounts.models import User,Address
import random

class MixinMockUser:
    user = None

    def _mock_user_data(self):
        data = {
            "username": "username_1",
            "password": "password_1",
            "mobile": "mobile_1",
        }
        self.user = User.objects.create_user(**data)
        self.address = Address.objects.create(
            user=self.user,
            name="Gavin",
            tel="11111",
            street="中国。北京"
        )


class MixinMockProduct:

    def _mock_product(self):
        new_cate = Category.objects.create(title="新的分类")

        self.mock_data = []  # 保存的是存在的数据
        for i in range(3):
            product = Product.objects.create(title=f"新的标题{i} {random.randint(1,10)}",
                                             category=new_cate,
                                             price=Decimal("600"),
                                             gender=2,
                                             type=1
                                             )
            self.mock_data.append(product)
            style = ProductStyle.objects.create(
                product=product,
                title="xxx",
                img="xxxxxxx"
            )
            self.product = product
            self.style = style


class MixinMockShopCart:

    def _mock_cart_cart(self):
        self.cart = ShopCart.objects.create(
            user=self.user,
            product=self.product,
            style=self.style,
            size="s",
        )

class MixinMockShopOrder:
    def _mock_cart_order(self):
        self.user0 = User.objects.create(**{
            "username": "username_0",
            "password": "password_0",

            "mobile": "mobile_0", })

        self.addr0 = Address.objects.create(
            user=self.user0,
            name=self.user0.username,
            tel="22221",
            street="中国。北京")
        self.order0 = ShopOrder.objects.create(
            user=self.user0,
            addr=self.addr0, )

        self.user2 = User.objects.create(**{"username": "username_2",
                                       "password": "password_2",
                                       "mobile": "mobile_2", })
        self.addr2 = Address.objects.create(
            user=self.user2,
            name=self.user2.username,
            tel="22231",
            street="中国。北京")
        self.order2 = ShopOrder.objects.create(
            user=self.user2,
            addr=self.addr2, )

class ProductTest(APITestCase):

    def setUp(self):
        # 创建假数据，准备测试环境
        new_cate = Category.objects.create(title="新的分类")

        self.mock_data = [] # 保存的是存在的数据
        for i in range(3):
            self.mock_data.append(
                Product.objects.create(title=f"新的标题{i} {random.randint(1,10)}",
                                       category=new_cate,
                                       price=Decimal("600"),
                                       gender=2,
                                       type=1)
            )
            pass

    def test_detail(self):
        """
        测试商品详情的API
        :return:
        """

        for obj in self.mock_data:  # 遍历数据库中数据

            # 1. 接口访问正常
            url = f"/store/product/{obj.id}/"  # obj.id 存在的商品id
            response = self.client.get(url)
            self.assertEqual(response.status_code, 200, "详情页API访问失败")  # 输入错正确的id，响应404
            # 404 url 没有设置，或者设置错了。 检查路由

            # 2. 接口返回内容正确
            content = response.data  # 接口返回内容
            content_str = str(response.data)
            key_list = [
                obj.get_gender_display(), # 1. 性别
                obj.get_type_display(), # 2. 类型
                obj.category.title, # 3. 分类
                obj.title, # 5. 标题
                obj.price,  # 5. 价格
                obj.sales_price,  # 5. 价格
                # 5. 其他。。。。
            ]

            for k in key_list:

                # if not str(k) in content_str:
                #     print("内容不正确，测试失败")  # 如果你要表现测试失败，请手机用断言
                self.assertTrue(str(k) in content_str, f'{k} not in content_str')

class ShopCartTest(MixinMockShopCart, MixinMockProduct, MixinMockUser, APITransactionTestCase):
    base_url = "/store/cart"

    def setUp(self):
        # 创建假数据，准备测试环境

        self._mock_product()
        # mock user
        self._mock_user_data()
        # mock cart
        self._mock_cart_cart()

        self.client.force_login(self.user)

    def test_list(self):
        url = self.base_url + '/'
        response = self.client.get(url)

        self.assertEqual(response.status_code, 200)

    def test_put(self):
        # todo mock 购物车数据

        self.cart.count = 10  # 购物车数量=10
        self.cart.save()

        url = self.base_url + f'/{self.cart.id}/'
        response = self.client.get(url)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['count'], 10)

        response = self.client.patch(url, {"count": 1})  # 修改数据的请求
        self.assertEqual(response.status_code, 200)

        #
        response = self.client.get(url)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['count'], 1)

    def test_delete(self):
        # todo mock 购物车数据
        url = self.base_url + f'/{self.cart.id}/'
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)

        response = self.client.delete(url)
        self.assertEqual(response.status_code, 204)  # 删除成功，没有数据可以返回

        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

    def test_new(self):
        # - 同样款式的商品，第一次添加创建数据，第二次添加时，不需要创造
        # - POST / cart / {模型字段}
        url = self.base_url + '/'
        data = {
            "product": self.product.id,
            "style": self.style.id,
            "size": "S",
            "count": random.randint(1, 10),
        }
        # - 删除所有的数据
        ShopCart.objects.all().delete()
        # - 第一次查询数据库的数量
        queryset = ShopCart.objects.all()
        count_1 = queryset.count()
        # - 第一次添加：
        response = self.client.post(url, data)
        # - 接口返回201
        self.assertEqual(response.status_code, 201)
        # - 数据库里，增加了数据（ 第二次查询数据库的数量，比上一次多)
        count_2 = queryset.count()
        obj_2 = queryset.last()
        self.assertGreater(count_2, count_1)
        self.assertEqual(count_2 - 1, count_1)

        # - 第二次添加：
        response = self.client.post(url, data)
        # - 接口返回201
        self.assertEqual(response.status_code, 201)
        # - 数据库，不增加数据 （ 第三次查询数据库的数量，和第二次一样）
        count_3 = queryset.count()
        obj_3 = queryset.last()
        self.assertEqual(count_3, count_2)

        # 修改已有数据的数量字段
        self.assertNotEqual(obj_2.count, obj_3.count)

 # 查询购物车中，选中的内容 （1， 选中， 2筛选）
    # 分析 （ 模型字段结构，逻辑细节，
    #   选中、筛选
    #     字段：标记是否选中
    #     字段：筛选数据
    #   - 接口
    #       - 选中某数据
    #       - 列表接口，增加查询条件
    def test_checked(self):
        # 1. mock 购物车
        #   2. 发起请求
        url = self.base_url + f'/{self.cart.id}/'
        response = self.client.patch(url, {"checked":1})
        self.assertEqual(response.status_code, 200)
    #   3， 该购物车条目，标记字段为选中
        cart = ShopCart.objects.get(id=self.cart.id)
        self.assertEqual(cart.checked, True)

    def test_check_filter(self):
        #         - 查询购物车中，选中的内容 （1， 选中， 2筛选）
        url = self.base_url + f'/'
        # 1. mock 购物车
        # 2. 发起请求，增加过滤条件
        response = self.client.get(url, {"checked": 1})
        # 3. 接口返回数量 = 0
        self.assertEqual(response.data['count'], 0)
        # 4. 讲数据 标记字段改为选中
        self.cart.checked = True
        self.cart.save()
        # 5. 发起请求，增加过滤条件
        response = self.client.get(url, {"checked": 1})
        # 6. 接口返回数量 = 1
        self.assertEqual(response.data['count'], 1)

class ShopOrderTest(MixinMockShopCart, MixinMockProduct, MixinMockUser, APITransactionTestCase):
    base_url = "/store/order"

    def setUp(self):
        # 创建假数据，准备测试环境
        self._mock_product()
        # mock user
        self._mock_user_data()
        # mock cart
        self._mock_cart_cart()
        # mock order
        # self._mock_cart_order()

        self.client.force_login(self.user)

    def test_new(self):
        url = self.base_url + '/'
        data = {
            "addr": self.address.id,  # 收货地址的id
            "item_list": [self.cart.id, ],

        }
        pass
        #     1. 必须选择收货地址， 如果没有，返回400
        response = self.client.post(url, {**data, **{"addr": ""}})
        self.assertEqual(response.status_code, 400)
        #         1. 收货地址必须存在
        #         2. 收货地址必须时当前用户
        #     # 2. 必须选择购物车条目，如果没有或者错误，返回400
        response = self.client.post(url, {**data, **{"item_list": []}})
        self.assertEqual(response.status_code, 400)
        #     1. 购物车条目，必须时存在的
        #     2. 购物车条目，必须时当前用户
        # 3. 输入正确的数据生成订单，订单数量、种类， 应该和购物车一致
        response = self.client.post(url, {**data, })
        self.assertEqual(response.status_code, 201, response.data)

        order = ShopOrder.objects.last()  # 返回最新创建的订单
        item_list = ShopItem.objects.filter(order=order)  # 返回该订单商品清单
        self.assertEqual(len(item_list), len(data['item_list']))
        item = item_list[0]

        self.assertEqual(self.cart.product, item.style.product)
        self.assertEqual(self.cart.style, item.style)
        self.assertEqual(self.cart.size, item.size)
        self.assertEqual(self.cart.count, item.count)

        # 4. 订单创建成功，对应的商品，从购物车移除
        with self.assertRaises(ShopCart.DoesNotExist):
            # 断言此处由异常
            ShopCart.objects.get(id=self.cart.id)
            # 重新获取购物车条目，发现 数据不存在

    def test_list(self):
    #         - 列出所有的订单， 列表接口 200 []
    #     self.client.logout()
        url = self.base_url + '/'
        order=ShopOrder.objects.create(user=self.user,addr=self.address)
        response=self.client.get(url)
        self.assertEqual(response.status_code,200)
        queryset=ShopOrder.objects.all()
        serializer=ShopOrderOutSerializer(queryset,many=True)
        self.assertEqual(response.data['results'],serializer.data)

    def test_id(self):
        url = self.base_url + '/'
        # 1. 每个人只能看到自己的订单
        # 前提条件
        #     1. 由多个用户
        #     2. 每个用户都有 订单

        user_A = User.objects.create(**{
            "username": "username_A",
            "password": "password_A",
            "mobile": "mobile_A",
        })
        addr_A = Address.objects.create(
            user=user_A,
            name=user_A.username,
            tel="11111",
            street="中国。北京"
        )
        order_A = ShopOrder.objects.create(
            user=user_A,
            addr=addr_A
        )

        #########
        user_B = User.objects.create(**{
            "username": "username_B",
            "password": "password_B",
            "mobile": "mobile_B",
        })
        addr_B = Address.objects.create(
            user=user_B,
            name=user_B.username,
            tel="11111",
            street="中国。北京"
        )
        order_B = ShopOrder.objects.create(
            user=user_B,
            addr=addr_B
        )


        ## 两个用户分别访问
        self.client.force_login(user_A) # 使用userA的身份进行访问
        response_A = self.client.get(url)

        self.client.force_login(user_B)  # 使用userA的身份进行访问
        response_B = self.client.get(url)


        # 断言结果
        self.assertNotEqual(response_A.data, response_B.data) # 两次访问结果不相等

        for data in response_A.data['results']:
            order = ShopOrder.objects.get(id=data['id']) # 根据data id 查询订单
            self.assertEqual(order.user_id, user_A.id) # 断言 该订单属于 userA

        for data in response_B.data['results']:
            order = ShopOrder.objects.get(id=data['id']) # 根据data id 查询订单
            self.assertEqual(order.user_id, user_B.id) # 断言 该订单属于 userA

        # 2. 订单属性进行筛选， 比如
        # 地址
        self.client.force_login(user_A)
        response_A = self.client.get(url, {"addr": addr_A.id})

        self.assertEqual(response_A.data['count'], 1)

        response_A = self.client.get(url, {"addr": 9999})
        self.assertEqual(response_A.data['count'], 0)

        # 状态
        response_A = self.client.get(url, {"status": order_A.status})
        self.assertEqual(response_A.data['count'], 1)

        response_A = self.client.get(url, {"status": 999})
        self.assertEqual(response_A.data['count'], 0)


        pass

    def test_detail(self):
        # 获取订单详情， 详情接口 200
        # get self.bae_url + '/{obj.id}/'
        order = ShopOrder.objects.create(
            addr=self.address,
            user=self.user,
        )

        url = self.base_url + f'/{order.id}/'

        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)

        serializer = ShopOrderOutSerializer(order)
        self.assertEqual(response.data, serializer.data)


    def test_stock(self):

        """库存控制"""
        url = self.base_url + '/'
        data = {
            "addr": self.address.id,  # 收货地址的id
            "item_list": [self.cart.id,],
        }

        # 1. 库存小于购买数量，购买不成功
        self.cart.product.stock = 1 # 库存 10
        self.cart.product.save()
        self.cart.count =2 #购买数量2
        self.cart.save()
        response = self.client.post(url, data) # 创建订单
        self.assertEqual(response.status_code, 400) # 购买失败

        # 1. 库存大于购买数量，购买成功
        self.cart.product.stock = 10  # 库存 10
        self.cart.product.save()
        self.cart.count = 2  # 购买数量2
        self.cart.save()
        response = self.client.post(url, data)  # 创建订单
        self.assertEqual(response.status_code, 201)  # 购买失败

        # 3，每次某买成功，要减少相对于的库存
        product = Product.objects.get(id=self.cart.product.id)
        self.assertEqual(product.stock, self.cart.product.stock-self.cart.count)